package com.che.software.testato.web.controller;

import java.io.Serializable;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

import org.apache.log4j.Logger;
import org.primefaces.event.NodeSelectEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.che.software.testato.business.ScriptItemManager;
import com.che.software.testato.business.ScriptManager;
import com.che.software.testato.business.exception.ScriptItemSearchManagerException;
import com.che.software.testato.business.exception.ScriptSearchManagerException;
import com.che.software.testato.business.exception.ScriptUpdateManagerException;
import com.che.software.testato.domain.entity.Hierarchy;
import com.che.software.testato.domain.entity.Script;
import com.che.software.testato.domain.entity.ScriptItem;
import com.che.software.testato.domain.entity.search.ScriptItemSearch;
import com.che.software.testato.domain.entity.search.ScriptSearch;
import com.che.software.testato.web.controller.acontroller.AbstractController;

/**
 * Controller used to manage the scripts visualisation.
 * 
 * @author Clement HELIOU (clement.heliou@che-software.com).
 * @copyright Che Software.
 * @license GNU General Public License.
 * @see AbstractController, Serializable.
 * @since July, 2011.
 * 
 *        This file is part of Testato.
 * 
 *        Testato is free software: you can redistribute it and/or modify it
 *        under the terms of the GNU General Public License as published by the
 *        Free Software Foundation, either version 3 of the License, or (at your
 *        option) any later version.
 * 
 *        Testato is distributed in the hope that it will be useful, but WITHOUT
 *        ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *        FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *        for more details.
 * 
 *        You should have received a copy of the GNU General Public License
 *        along with Testato. If not, see <http://www.gnu.org/licenses/>.
 * 
 *        Testato's logo is a creation of Arrioch
 *        (http://arrioch.deviantart.com/) and it's distributed under the terms
 *        of the Creative Commons License.
 */
@Component("scriptsVisualisationController")
@ManagedBean(name = "scriptsVisualisationController")
@Scope("session")
@SessionScoped
public class ScriptsVisualisationController extends AbstractController implements Serializable {

	/**
	 * Constants.
	 */
	private static final Logger LOGGER = Logger.getLogger(ScriptsVisualisationController.class);
	private static final long serialVersionUID = -3516396438309689859L;
	private static final String NODE_TYPE_ITEM = "ite", NODE_TYPE_SCRIPT = "scr";

	/**
	 * Member.
	 */
	private Hierarchy currentHierarchy;
	private List<Script> hiearchyScripts, selectedItemScripts;
	private List<ScriptItem> selectedScriptItems;
	private Script currentScript, currentEditedScript;
	@Autowired
	private transient ScriptItemManager scriptItemManager;
	@Autowired
	private transient ScriptManager scriptManager;
	private TreeNode root, selectedNode;

	/**
	 * Method call to update a script label.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @return null because none redirection is necessary.
	 * @since August, 2011.
	 */
	public String onEditScriptLabel() {
		LOGGER.debug("onEditScriptLabel(" + currentEditedScript.getScriptId() + ").");
		try {
			scriptManager.updateScript(currentEditedScript);
		} catch (ScriptUpdateManagerException e) {
			LOGGER.error("Error during the script updating.", e);
			hiearchyScripts = null; // To trigger the data loading.
		}
		return null;
	}

	/**
	 * Event listener on node click.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @param event the event launched.
	 * @since July, 2011.
	 */
	public void onNodeSelect(NodeSelectEvent event) {
		LOGGER.debug("onNodeSelect().");
		selectedItemScripts = null;
		selectedScriptItems = null; // To trigger the data loading.
	}

	/**
	 * Method call to get the scripts elicitation page.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @return the url to follow to get the page.
	 * @since July, 2011.
	 */
	public String onReturn() {
		LOGGER.debug("onReturn().");
		return "scriptsElicitation.xhtml?faces-redirect=true";
	}

	/**
	 * Method call to get the script details page.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @return the url to follow to get the page.
	 * @since July, 2011.
	 */
	public String onScriptDetails() {
		LOGGER.debug("onScriptDetails().");
		selectedItemScripts = null;
		selectedScriptItems = null;
		root = null; // To trigger the data loading.
		return "scriptDetails.xhtml?faces-redirect=true";
	}

	/**
	 * Method call to get the scripts visualization page.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @return the url to follow to get the page.
	 * @since July, 2011.
	 */
	public String onScriptsVisualisation() {
		LOGGER.debug("onScriptsVisualisation().");
		return "scriptsVisualization.xhtml?faces-redirect=true";
	}

	/**
	 * Build the tree for a given script item.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @param parentScriptItem the parent script item id.
	 * @param parentNode the parent node.
	 * @throws ScriptItemSearchManagerException if an error occurs during the
	 *         script item search.
	 * @throws ScriptSearchManagerException if an error occurs during the script
	 *         search.
	 * @since July, 2011.
	 */
	private void getTreeNodeFromScriptItem(ScriptItem parentScriptItem, TreeNode parentNode) throws ScriptItemSearchManagerException, ScriptSearchManagerException {
		LOGGER.debug("getTreeNodeFromScriptItem(" + parentScriptItem.getScriptItemId() + "," + parentNode.getData().toString() + ").");
		ScriptSearch searchBean = new ScriptSearch();
		searchBean.setParentScriptItem(parentScriptItem.getScriptItemId());
		for (Script script : scriptManager.searchScript(searchBean)) {
			TreeNode currentScriptNode = new DefaultTreeNode(NODE_TYPE_SCRIPT, script, parentNode);
			ScriptItemSearch searchScriptItemBean = new ScriptItemSearch();
			searchScriptItemBean.setScriptId(script.getScriptId());
			for (ScriptItem scriptItem : scriptItemManager.searchScriptItem(searchScriptItemBean)) {
				TreeNode createdNode = new DefaultTreeNode(NODE_TYPE_ITEM, scriptItem, currentScriptNode);
				getTreeNodeFromScriptItem(scriptItem, createdNode);
			}
		}
	}

	/**
	 * Getter for the private field value currentHierarchy.
	 * 
	 * @return the currentHierarchy field value.
	 */
	public Hierarchy getCurrentHierarchy() {
		return currentHierarchy;
	}

	/**
	 * Setting a value to the currentHierarchy field.
	 * 
	 * @param currentHierarchy the value to set.
	 */
	public void setCurrentHierarchy(Hierarchy currentHierarchy) {
		this.currentHierarchy = currentHierarchy;
	}

	/**
	 * Getter for the private field value hiearchyScripts.
	 * 
	 * @return the hiearchyScripts field value.
	 */
	public List<Script> getHiearchyScripts() {
		if (null == hiearchyScripts) {
			LOGGER.debug("getHiearchyScripts(): initialization.");
			try {
				hiearchyScripts = scriptManager.searchScript(new ScriptSearch(currentHierarchy.getHierarchyId()));
			} catch (ScriptSearchManagerException e) {
				LOGGER.error("Error during the hierarchy scripts recovery.", e);
			}
		}
		return hiearchyScripts;
	}

	/**
	 * Setting a value to the hiearchyScripts field.
	 * 
	 * @param hiearchyScripts the value to set.
	 */
	public void setHiearchyScripts(List<Script> hiearchyScripts) {
		this.hiearchyScripts = hiearchyScripts;
	}

	/**
	 * Getter for the private field value scriptManager.
	 * 
	 * @return the scriptManager field value.
	 */
	public ScriptManager getScriptManager() {
		return scriptManager;
	}

	/**
	 * Setting a value to the scriptManager field.
	 * 
	 * @param scriptManager the value to set.
	 */
	public void setScriptManager(ScriptManager scriptManager) {
		this.scriptManager = scriptManager;
	}

	/**
	 * Getter for the private field value currentScript.
	 * 
	 * @return the currentScript field value.
	 */
	public Script getCurrentScript() {
		return currentScript;
	}

	/**
	 * Setting a value to the currentScript field.
	 * 
	 * @param currentScript the value to set.
	 */
	public void setCurrentScript(Script currentScript) {
		this.currentScript = currentScript;
	}

	/**
	 * Getter for the private field value root.
	 * 
	 * @return the root field value.
	 */
	public TreeNode getRoot() {
		if (null == root) {
			LOGGER.debug("getRoot().");
			root = new DefaultTreeNode("Root", null);
			TreeNode currentScriptNode = new DefaultTreeNode(NODE_TYPE_SCRIPT, currentScript, root);
			ScriptItemSearch searchBean = new ScriptItemSearch();
			searchBean.setScriptId(currentScript.getScriptId());
			try {
				for (ScriptItem scriptItem : scriptItemManager.searchScriptItem(searchBean)) {
					TreeNode createdNode = new DefaultTreeNode(NODE_TYPE_ITEM, scriptItem, currentScriptNode);
					getTreeNodeFromScriptItem(scriptItem, createdNode);
				}
			} catch (ScriptItemSearchManagerException e) {
				LOGGER.error("Error during a scrip item recovery.", e);
			} catch (ScriptSearchManagerException e) {
				LOGGER.error("Error during a script recovery.", e);
			}
		}
		return root;
	}

	/**
	 * Setting a value to the root field.
	 * 
	 * @param root the value to set.
	 */
	public void setRoot(TreeNode root) {
		this.root = root;
	}

	/**
	 * Getter for the private field value scriptItemManager.
	 * 
	 * @return the scriptItemManager field value.
	 */
	public ScriptItemManager getScriptItemManager() {
		return scriptItemManager;
	}

	/**
	 * Setting a value to the scriptItemManager field.
	 * 
	 * @param scriptItemManager the value to set.
	 */
	public void setScriptItemManager(ScriptItemManager scriptItemManager) {
		this.scriptItemManager = scriptItemManager;
	}

	/**
	 * Getter for the private field value nodeTypeItem.
	 * 
	 * @return the nodeTypeItem field value.
	 */
	public String getNodeTypeItem() {
		return NODE_TYPE_ITEM;
	}

	/**
	 * Getter for the private field value nodeTypeScript.
	 * 
	 * @return the nodeTypeScript field value.
	 */
	public String getNodeTypeScript() {
		return NODE_TYPE_SCRIPT;
	}

	/**
	 * Getter for the private field value selectedItemScripts.
	 * 
	 * @return the selectedItemScript field value.
	 */
	public List<Script> getSelectedItemScripts() {
		if (null == selectedItemScripts && null != selectedNode && selectedNode.getData() instanceof ScriptItem) {
			LOGGER.debug("getSelectedItemScripts(): initialization.");
			ScriptSearch searchBean = new ScriptSearch();
			searchBean.setParentScriptItem(((ScriptItem) selectedNode.getData()).getScriptItemId());
			try {
				selectedItemScripts = scriptManager.searchScript(searchBean);
			} catch (ScriptSearchManagerException e) {
				LOGGER.error("Error during a scripts recovery.", e);
			}
		}
		return selectedItemScripts;
	}

	/**
	 * Setting a value to the selectedItemScript field.
	 * 
	 * @param selectedItemScript the value to set.
	 */
	public void setSelectedItemScripts(List<Script> selectedItemScripts) {
		this.selectedItemScripts = selectedItemScripts;
	}

	/**
	 * Getter for the private field value selectedScriptItems.
	 * 
	 * @return the selectedScriptItems field value.
	 */
	public List<ScriptItem> getSelectedScriptItems() {
		if (null == selectedScriptItems && null != selectedNode && selectedNode.getData() instanceof Script) {
			LOGGER.debug("getSelectedScriptItems(): initialization.");
			ScriptItemSearch searchBean = new ScriptItemSearch();
			searchBean.setScriptId(((Script) selectedNode.getData()).getScriptId());
			try {
				selectedScriptItems = scriptItemManager.searchScriptItem(searchBean);
			} catch (ScriptItemSearchManagerException e) {
				LOGGER.error("Error during a script items recovery.", e);
			}
		}
		return selectedScriptItems;
	}

	/**
	 * Setting a value to the selectedScriptItems field.
	 * 
	 * @param selectedScriptItems the value to set.
	 */
	public void setSelectedScriptItems(List<ScriptItem> selectedScriptItems) {
		this.selectedScriptItems = selectedScriptItems;
	}

	/**
	 * Getter for the private field value selectedNode.
	 * 
	 * @return the selectedNode field value.
	 */
	public TreeNode getSelectedNode() {
		return selectedNode;
	}

	/**
	 * Setting a value to the selectedNode field.
	 * 
	 * @param selectedNode the value to set.
	 */
	public void setSelectedNode(TreeNode selectedNode) {
		this.selectedNode = selectedNode;
	}

	/**
	 * Getter for the private field value currentEditedScript.
	 * 
	 * @return the currentEditedScript field value.
	 */
	public Script getCurrentEditedScript() {
		return currentEditedScript;
	}

	/**
	 * Setting a value to the currentEditedScript field.
	 * 
	 * @param currentEditedScript the value to set.
	 */
	public void setCurrentEditedScript(Script currentEditedScript) {
		this.currentEditedScript = currentEditedScript;
	}
}